/*
 * Author: QinTH
 * Date: 2012/03/04
 * Email: qthdaidai@qq.com
 * */

#include <stdarg.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <unistd.h>

#include "common.h"
#include "core_conf.h"
#include "log.h"
#include "string_help.h"
#include "usage.h"
#include "recv_init.h"
#include "task.h"
#include "core_conf_map.h"
#include "task_manager.h"
#include "task_default.h"
#include "msg.h"
#include "screen.h"
#include "info.h"
#include "core_data.h"
#include "mail.h"
#include "db_info.h"
#include "db_ops.h"

struct core_conf core_conf = {
	.core_conf_file = "./test/config",

	.old_mode = 0, /* 0: use new core. 1:use old core. */
	.old_mode_argc = 0,
	.old_mode_argv = NULL,
	.old_mode_core_path = "./sklcc-dpi",

	.pcap_mode = 2, /* 0: use file. 1: use net interface. 2: use socket to get packet -1: wrong. */
	.pcap_file = NULL,
	.pcap_interface = NULL,
	.pcap_handle = NULL,
	.task_manager = NULL,
	.core_data_queue = NULL,

	.usleep_time = 10,

	.use_mm = 1,
	.mm_limit = 2147483647, /* 2Gb - 1 = 2 * 1024 * 1024 * 1024 - 1b */

	.max_id = 5000000,
	.mode = 1,

	.use_mail = 1,

	.db_info = NULL,
	.db_count = 0,

	.server_conf=NULL,
	.s=-1,
	.addr=NULL,
};

/* It will be used in parse_options_aboute_new_core and get_config_from_file. */
static int db_count = 0;

struct serversocket_conf *get_server(char *filename)
{
        FILE *fp;
        struct serversocket_conf *conf;
	conf=(struct serversocket_conf *)malloc(sizeof(struct serversocket_conf));

        if((fp=fopen(filename,"r"))==NULL)
        {
                printf("open file fail");
                exit(0);
        }

        char a[2][20];
        char b[2][20];

        fgets(a[0],19,fp);
        fgets(a[1],19,fp);
        int i=0;
        int j=0;

        for(i=0;i<strlen(a[0]);i++)
        {
                if(a[0][i]==':')
                {
                        for(j=0;j<strlen(a[0])-i;j++)
                        {
                                b[0][j]=a[0][j+i+1];
                        }
                }
        }

        for(i=0;i<strlen(a[1]);i++)
        {
                if(a[1][i]==':')
                {
                        for(j=0;j<strlen(a[1])-i;j++)
			{
				b[1][j]=a[1][j+i+1];
                        }
                }
        }

        conf->name="Dispather";
        conf->port=atoi(b[0]);
        conf->ip=inet_addr(b[1]);

        return conf;
}

static void parse_options_about_new_core(int argc, char *argv[])
{
	log_info("begin parse_options_about_new_core.");
	/* if our program call this function, we must have at least one args */
	if (argc == 1 || argv == NULL) {
		log_error("in parse_options_about_new_core, but argc <= 1 or argv == NULL.");
		exit(-1);
	}

	int opt;
	while ((opt = getopt(argc, argv, "l:c:u:m:f:t:a:b:d:s:h")) != EOF) {
		switch (opt) {
		case 'l' :
			{
				if (core_conf.pcap_mode != -1) {
					break;
				}	

				/* fixme: pcapl won't be free... */
				char *pcapl = (char *)malloc(sizeof(char) * 128);
				if (pcapl == NULL) {
					log_error("no enough mem.");
					exit(-1);
				}

				strcpy(pcapl, optarg);
				core_conf.pcap_file = pcapl;
				break;
			}	
		case 'c' :
			{
				if (core_conf.pcap_mode != 1) {
					break;
				}

				/* fixme: pcapc won't be free... */
				char *pcapc = (char *)malloc(sizeof(char) * 128);
				if (pcapc == NULL) {
					log_error("no enough mem.");
					exit(-1);
				}

				strcpy(pcapc, optarg);
				core_conf.pcap_interface = pcapc;
				break;
			}
		case 'u' :
			{
				int ut = 0;
				ut = atoi(optarg);
				if (core_conf.usleep_time != ut) {
					core_conf.usleep_time = ut;	
				}
				break;
			}
		case 'd' :
			{
				char *to = (char *)malloc(MAIL_TO_MAX_LEN);
				if (to == NULL) {
					log_error("no enough mem.");
					exit(-1);
				}
				strcpy(to, optarg);
				core_conf.to = to;
				break;
			}
		case 's' :
			{
				if (db_count < DB_SUPPORT_MAX_NUM) {
					strncpy(core_conf.db_config[db_count], optarg, DB_CONFIGURE_MAX_LEN);
					core_conf.db_config[db_count][DB_CONFIGURE_MAX_LEN - 1] = '\0';
					db_count++;
					core_conf.db_count++;
					break;
				}
			}
		case 'b' :
			{
				int um = 0;
				um = atoi(optarg);
				core_conf.use_mail = um;
				break;
			}
		case 'a' :
			{
				int fm = 0;
				fm = atoi(optarg);
				core_conf.mode = fm;
				break;
			}
		case 't' :
			{
				unsigned long mi = 0;
				mi = atol(optarg);
				core_conf.max_id = mi;
				break;
			}
		case 'm' :
			{
				unsigned long ml = 0;
				ml = atol(optarg);
				if (core_conf.mm_limit != ml) {
					core_conf.mm_limit = ml;	
				}
				break;
			}
		case 'h':
		default :
			{
				usage();
				exit(-1);	
				/* break; */
			}
		}
	}
}

static void parse_options(int argc, char *argv[])
{
	log_info("begin parse_options.");
	if (argc < 1 || argv == NULL) {
		log_print("at least one argument.");
		usage();
		exit(-1);
	}	

	if (argc == 1) {
		return ;
	}

	/* we need to be compatible with old core. */
	if (strcmp(argv[1], "-f") == 0) {
		log_info("'-f' use old core.");
		core_conf.old_mode = 1;
		core_conf.old_mode_argc = argc - 2;
		core_conf.old_mode_argv = argv + 2;
		return ;
	} else { /* now, we can use our new core. */
		log_info("use new core.");
		parse_options_about_new_core(argc, argv);
	}
}

static int value_to_index(char *value, char (*map)[64], int n)
{
	if (value == NULL || map == NULL) {
		return -1;
	}

	int i;
	for (i = 0; i < n; i++) {
		if (strcmp(value, map[i]) == 0) {
			return i;
		}
	}
	return -1;
}

static void get_config_from_file(void)
{
	log_info("begin get_config_from_file.");
	#define MAX_LINE 1024
	char buf[MAX_LINE];
	FILE *fp;
	
	if ((fp = fopen(core_conf.core_conf_file, "r")) == NULL) {
		log_warning("cann't open core config file, use default config.");
		return ;
	}

	core_conf.server_conf=get_server("./conf/server");

	while (fgets(buf, MAX_LINE, fp) != NULL) {
		char lvalue[256] = {'\0'};		
		char rvalue[256] = {'\0'};

		if (buf[0] == '#' || buf[0] == '\n') {
			continue;
		}

		sscanf(buf, "%s = %s\n", lvalue, rvalue);

		if (rvalue == NULL || lvalue == NULL || strcmp(rvalue, "\0") == 0 || strcmp(rvalue, " ") ==  0) {
			continue;
		}
		
		int lvalue_index = -1;
		lvalue_index = value_to_index(lvalue, core_conf_map, END_OF_CORE_CONF_INDEX);
		log_info("lvalue_index = %d", lvalue_index);
		
		switch (lvalue_index) {
		case CORE_CONF_FILE :
			{
				// fixme: ccf won't be free... 
				char *ccf = (char *)malloc(sizeof(char) * 128);	
				if (ccf == NULL) {
					log_error("no enough mem.");
					exit(-1);
				}
				strcpy(ccf, rvalue);
				core_conf.core_conf_file = ccf;
				break;
			}
		case OLD_MODE : 
			{
				int om = 0;
				om = atoi(rvalue);		
				core_conf.old_mode = om;
				break;
			} 
		case OLD_MODE_ARGC :
		case OLD_MODE_ARGV :
			break;
		case OLD_MODE_CORE_PATH :
			{
				// fixme: omcp won't be free... 
				char *omcp = (char *)malloc(sizeof(char) * 128);
				if (omcp == NULL) {
					log_error("no enough mem.");
					exit(-1);
				}
				strcpy(omcp, rvalue);
				core_conf.old_mode_core_path = omcp;
				break;
			}
		case DB_CONF :
			{
				if (db_count < DB_SUPPORT_MAX_NUM) {
					strncpy(core_conf.db_config[db_count], rvalue, DB_CONFIGURE_MAX_LEN);
					core_conf.db_config[db_count][DB_CONFIGURE_MAX_LEN - 1] = '\0';
					db_count++;
					core_conf.db_count++;
					break;	
				}	
			}
		case PCAP_MODE :
			{
				int pm = -1;
				pm = atoi(rvalue);		
				core_conf.pcap_mode = pm;
				break;
			}
		case MODE :
			{
				int fm = -1;
				fm = atoi(rvalue);		
				core_conf.mode = fm;
				break;
			}
		case PCAP_FILE :
			{
				// fixme: pf won't be free... 
				char *pf = (char *)malloc(sizeof(char) * 128);
				if (pf == NULL) {
					log_error("no enough mem.");
					exit(-1);
				}
				strcpy(pf, rvalue);
				core_conf.pcap_file = pf;
				break;
			}
		case PCAP_INTERFACE :
			{
				// fixme: pf won't be free... 
				char *pi = (char *)malloc(sizeof(char) * 128);
				if (pi == NULL) {
					log_error("no enough mem.");
					exit(-1);
				}
				strcpy(pi, rvalue);
				core_conf.pcap_interface = pi;
				break;
			}
		case TO :
			{
				char *to = (char *)malloc(MAIL_TO_MAX_LEN);
				if (to == NULL) {
					log_error("no enough mem.");
					exit(-1);
				}
				strcpy(to, rvalue);
				core_conf.to = to;
				break;
			}
		case USLEEP_TIME :
			{
				int ut = -1;
				ut = atoi(rvalue);		
				core_conf.usleep_time = ut;
				break;
			}
		case USE_MM :
			{
				int um = -1;
				um = atoi(rvalue);		
				core_conf.use_mm = um;
				break;
			}
		case MM_LIMIT : 
			{
				unsigned long ml;
				ml = atol(rvalue);
				core_conf.mm_limit = ml;
				break;
			}
		case MAX_ID : 
			{
				unsigned long mi;
				mi = atol(rvalue);
				core_conf.max_id = mi;
				break;
			}
		case USE_MAIL : 
			{
				unsigned long um;
				um = atoi(rvalue);
				core_conf.use_mail = um;
				break;
			}
		default :
			break;
		}
	}
}

static void get_core_config(int argc, char *argv[])
{
	get_config_from_file();
	parse_options(argc, argv);
}

static void init1(int argc, char *argv[])
{
	clear_screen();

	log_open(TASK_NAME);
	
	if (argc == 0 || argv == NULL) {
		log_error("init 1 failed.");
		exit(-1);
	}

	get_core_config(argc, argv);

	log_info("call init1 successfully.");

	sklcc_dpi_platform_info();
}

static void finish1(void)
{
	log_info("begin finish1.");
	log_close();
}

static void init2(void)
{
	log_info("begin init2.");
	log_print("begin system init...");
	/* such as init pcap, db. */
	if (core_conf.old_mode == 0) {
		log_print("begin task manager create...");
		core_conf.task_manager = task_manager_create();
		if (core_conf.task_manager == NULL) {
			log_error("task_manager_create failed.");
			log_print("task_manager_create failed.");
			exit(-1);
		}
		log_print("task manager create successful.");

		log_print("begin core data queue init...");
		core_conf.core_data_queue = core_data_queue_create();
		if (core_conf.core_data_queue == NULL) {
			log_error("core_data_queue_create failed.");
			log_print("core_data_queue_create failed.");
			exit(-1);
		}

		core_conf.core_data_queue->max_id = core_conf.max_id;
		core_conf.core_data_queue->mode = core_conf.mode;
		log_print("core data queue init successful.");

		log_print("begin task manager init...");
		int ret = 0;
		ret = task_manager_init(core_conf.task_manager, &core_conf);
		if (ret != 0) {
			log_print("cann't init task_manager.");
			log_error("cann't init task_manager, ret = %d", ret);
			exit(-1);
		}

		log_print("begin add default task...");
		/* add default task, not start them. */
		ret = default_task_init(core_conf.task_manager);
		if (ret != 0) {
			log_print("cann't add default task.");
			log_error("cann't add default task, ret = %d", ret);
			exit(-1);
		}
		log_print("add default task successful.");
		log_print("task manager init successful.");

		log_print("begin pcap handle init...");
		int init;

		init = recv_init();

		if (init==0) {
			log_error("can't init pcap hanlde offline");
			exit(0);
		}
		else if(init==2)
		{
			log_error("can't init socket");
			exit(0);
		}
		else if(init>=4)
		{
			log_error("the mode that not defined");
			exit(0);
		}

		if(init==1)
		{
			core_conf.pcap_datalink_type = pcap_datalink(core_conf.pcap_handle);
		}
		else
		{
			core_conf.pcap_datalink_type = DLT_EN10MB ;
		}

//		log_print("pcap handle init successful.");

		log_print("begin db init...");
		
		core_conf.db_info = db_info_create();
		if (core_conf.db_info == NULL) {
			log_error("db_info init failed.");
			exit(-1);
		}
		int db_i = 0;
		for ( ; db_i < core_conf.db_count; db_i++) {
			char tmp[5][128];
			string_break(core_conf.db_config[db_i], tmp);	
			add_db_data(core_conf.db_info, tmp[0],	/* db_type_name */
				       			tmp[1],	/* db_name */ 
							tmp[2], /* user_name */ 
							tmp[3], /* passwed */ 
							tmp[4]);/* address */
		}

		log_print("db init successful.");
	}

	log_print("system init successful.");

	print_config();
}

static void process(void)
{
	log_info("begin process.");

	if (core_conf.old_mode == 1) {
		char cmd1[1024] = {'\0'};
		char cmd2[1024] = {'\0'};
		int ret = 0;
		
		string_pp_to_p_with_space(cmd1, core_conf.old_mode_argc, core_conf.old_mode_argv);
		sprintf(cmd2, "%s %s", core_conf.old_mode_core_path, cmd1);

		/* execuate old core, such as "./sklcc -i eth0 -o ./opendpi.sdp" */
		ret = system(cmd2);
		log_info("system()'s return value: %d", ret);
	} else { /* execuate new core */
		start_default_task(&core_conf);

		while (1) {
			//printf("idle...\n");
			sleep(1000);
		}

		log_print("Oh...Bug...");
	}
}
/*
void init_script()
{
	char str[100];
	int str_str=0;
	str_str=sprintf(str,"%s","./configure");
	if(str_str==0)
	{
		printf("init db fail or init rrd fail");
	}
	system(str);
	
}
*/
int main(int argc, char *argv[])
{
	//init all script include init_db.py and rrd_init.sh
	
//	init_script();

	//simple init

	init1(argc, argv);

	/* real init */
	init2();

	process();

	finish1();

	return 0;
}
